#include "Tokenizer.h"
#include "Errors.h"

using namespace Error;

#define Add(state) Tokenizer::IState* Tokenizer::state::Instance = new state()
#define InitState(state) static_cast<state*>(state::Instance)->Init(this)

Add(Error);
Add(Start);
Add(WhiteSpace);
Add(Name);
Add(Name::End);
Add(Integer);
Add(Integer::End);
Add(Integer::Error);
Add(Fraction);
Add(Fraction::End);
Add(Fraction::Error);
Add(Fraction_Exponent);
Add(Fraction_Exponent::Sign);
Add(String);
Add(String::End);
Add(String::Error);
Add(String::Escapement);
Add(Char);
Add(Char::End);
Add(Char::Error);
Add(Char::Escapement);
Add(Operator);
Add(Operator::End);
Add(Operator_Slesh);
Add(Operator_PrefixEqual);
Add(Operator_PrefixDoubleEqual);
Add(Operator_Multiply);
Add(Operator_Less);
Add(Operator_More);
Add(Operator_Plus);
Add(Operator_Minus);
Add(Operator_And);
Add(Operator_Or);
Add(Comment);
Add(Comment::End);
Add(MultiLineComment);
Add(MultiLineComment::End);
Add(MultiLineComment_PreEnd);

bool Tokenizer::Init()
{
	i = Code.begin();
	line = 1;
	length = Code.length();
	if (!length )
	{
		return false;
	}
	c = *i;

	InitState(Error);
	InitState(Start);
	InitState(WhiteSpace);
	InitState(Name);
	InitState(Name::End);
	InitState(Integer);
	InitState(Integer::End);
	InitState(Integer::Error);
	InitState(Fraction);
	InitState(Fraction::End);
	InitState(Fraction::Error);
	InitState(Fraction_Exponent);
	InitState(Fraction_Exponent::Sign);
	InitState(String);
	InitState(String::End);
	InitState(String::Error);
	InitState(String::Escapement);
	InitState(Char);
	InitState(Char::End);
	InitState(Char::Error);
	InitState(Char::Escapement);
	InitState(Operator);
	InitState(Operator::End);
	InitState(Operator_Slesh);
	InitState(Operator_PrefixEqual);
	InitState(Operator_PrefixDoubleEqual);
	InitState(Operator_Multiply);
	InitState(Operator_Less);
	InitState(Operator_More);
	InitState(Operator_Plus);
	InitState(Operator_Minus);
	InitState(Operator_And);
	InitState(Operator_Or);
	InitState(Comment);
	InitState(Comment::End);

	InitState(MultiLineComment);
	InitState(MultiLineComment::End);
	InitState(MultiLineComment_PreEnd);

	return true;
}

void Tokenizer::IState::Init(Tokenizer* t)
{
	tok = t;
}

SimpleToken* Tokenizer::State::Advance()
{
	if (tok->c == '\n')
		tok->line++;

	tok->str += tok->c;
	tok->i++;
	tok->c = *tok->i;
	return to[tok->c]->Advance();
}

SimpleToken* Tokenizer::Start::Advance()
{
	tok->str = "";
	return to[tok->c]->Advance();
}

SimpleToken* Tokenizer::WhiteSpace::Advance()
{
	if (tok->c == '\n')
		tok->line++;

	tok->i++;
	tok->c = *tok->i;
	return Start::Instance->Advance();
}

SimpleToken* Tokenizer::Name::End::Advance()
{
	const char* value = tok->Names[tok->str];
	if (!value)
	{
		NamesIterator i = tok->Names.find(tok->str);
		value = i->second = i->first.c_str();
	}
	
	return new SimpleToken(SimpleToken::Name, value, 0,0,tok->line);
}

SimpleToken* Tokenizer::Integer::End::Advance()
{
	const char* value = tok->Names[tok->str];
	if (!value)
	{
		NamesIterator i = tok->Names.find(tok->str);
		value = i->second = i->first.c_str();
	}
	
	return new SimpleToken(SimpleToken::Integer,value, 0, 0,tok->line);
}

SimpleToken* Tokenizer::Fraction::End::Advance()
{
	const char* value = tok->Names[tok->str];
	if (!value)
	{
		NamesIterator i = tok->Names.find(tok->str);
		value = i->second = i->first.c_str();
	}

	return new SimpleToken(SimpleToken::Float,value, 0, 0,tok->line);
}

SimpleToken* Tokenizer::Integer::Error::Advance()
{
	SimpleToken(SimpleToken::Integer, tok->str.c_str(), 0, 0, tok->line).error(BadNumber);
	return NULL;
}

SimpleToken* Tokenizer::Error::Advance()
{
	tok->str += tok->c;
	SimpleToken(SimpleToken::Operator, tok->str.c_str(), 0, 0, tok->line).error("BadCharacter");
	return NULL;
}

SimpleToken* Tokenizer::Fraction::Error::Advance()
{
	SimpleToken(SimpleToken::Integer, tok->str.c_str(), 0, 0, tok->line).error(BadExponent);
	return NULL;
}

#ifdef WITH_ESCAPEMENT
SimpleToken* Tokenizer::String::Advance()
{
	switch (tok->c)
	{
		case '"':
		case '\\':
			break;
		default:
			tok->str += tok->c;
	}
	tok->i++;
	tok->c = *tok->i;
	return to[tok->c]->Advance();
}
#endif

SimpleToken* Tokenizer::String::End::Advance()
{
#ifndef WITH_ESCAPEMENT
	tok->str += tok->c;
#endif

	tok->i++;
	tok->c = *tok->i;

	const char* value = tok->Names[tok->str];
	if (!value)
	{
		NamesIterator i = tok->Names.find(tok->str);
		value = i->second = i->first.c_str();
	}
	
	return new SimpleToken(SimpleToken::String,value, 0,0,tok->line);
}

SimpleToken* Tokenizer::String::Escapement::Advance()
{
#ifdef WITH_ESCAPEMENT
	char c = tok->c;
	switch (c) 
	{
		case 'b':
			tok->str += '\b';
			break;
		case 'f':
			tok->str += '\f';
			break;
		case 'n':
			tok->str += '\n';
			break;
		case 'r':
			tok->str += '\r';
			break;
		case 't':
			tok->str += '\t';
			break;
		case 'u':
			if (tok->i + 5 >= tok->Code.end()) {
				SimpleToken(SimpleToken::Integer, tok->str.c_str(), tok->from, 0, tok->line).error(UnterminatedString);
			}
			sscanf(string(tok->i + 1, tok->i + 4).c_str(),"%d",&c); 
			tok->i += 4;
			break;
		default:
			tok->str += c;
	}
#else
	if (tok->c == 'u')
	{
		if (tok->i + 5 >= tok->Code.end()) {
			SimpleToken(SimpleToken::Integer, tok->str.c_str(), tok->from, 0, tok->line).error(UnterminatedString);
		}
	}
	tok->str += tok->c;
#endif
	tok->i++;
	tok->c = *tok->i;
	return to[tok->c]->Advance();
}

SimpleToken* Tokenizer::String::Error::Advance()
{
	SimpleToken(SimpleToken::String, tok->str.c_str(), tok->from, 0, tok->line).error(tok->c == '\n' || tok->c == '\r' ?
						UnterminatedString :
						ControlInString); 
	return NULL;
}

#ifdef WITH_ESCAPEMENT
SimpleToken* Tokenizer::Char::Advance()
{
	switch (tok->c)
	{
		case '"':
		case '\\':
			break;
		default:
			tok->str += tok->c;
	}
	tok->i++;
	tok->c = *tok->i;
	return to[tok->c]->Advance();
}
#endif

SimpleToken* Tokenizer::Char::Error::Advance()
{
	return NULL;
}

SimpleToken* Tokenizer::Char::Escapement::Advance()
{
#ifdef WITH_ESCAPEMENT
	char c = tok->c;
	switch (c) 
	{
		case 'b':
			tok->str += '\b';
			break;
		case 'f':
			tok->str += '\f';
			break;
		case 'n':
			tok->str += '\n';
			break;
		case 'r':
			tok->str += '\r';
			break;
		case 't':
			tok->str += '\t';
			break;
		case 'u':
			if (tok->i + 5 >= tok->Code.end()) 
			{
				SimpleToken(SimpleToken::Char, tok->str.c_str(), tok->from,0,tok->line).error(UnterminatedString);
			}
			sscanf(string(tok->i + 1,tok->i + 4).c_str(),"%d",&c); 
			tok->i += 4;
			break;
		default:
			tok->str += c;
	}
#else
	if (tok->c == 'u')
	{
		if (tok->i + 5 >= tok->Code.end()) {
			SimpleToken(SimpleToken::Integer, tok->str.c_str(), tok->from, 0, tok->line).error(UnterminatedString);
		}
	}
	tok->str += tok->c;
#endif
	tok->i++;
	tok->c = *tok->i;
	return to[tok->c]->Advance();
}

SimpleToken* Tokenizer::Char::End::Advance()
{
#ifndef WITH_ESCAPEMENT
	tok->str += tok->c;
#endif

	tok->i++;
	tok->c = *tok->i;

	const char* value = tok->Names[tok->str];
	if (!value)
	{
		NamesIterator i = tok->Names.find(tok->str);
		value = i->second = i->first.c_str();
	}

	return new SimpleToken(SimpleToken::Char,value, 0,0,tok->line);
}


SimpleToken* Tokenizer::Comment::Advance()
{	
	tok->i++;
	tok->c = *tok->i;
	return to[tok->c]->Advance();
}

SimpleToken* Tokenizer::Comment::End::Advance()
{
	return Start::Instance->Advance();
}

SimpleToken* Tokenizer::MultiLineComment::Advance()
{
	if (tok->AtEnd())
		SimpleToken(SimpleToken::Name,"", 0,0,tok->line).error(UnterminatedComment);

	if (tok->c == '\n')
		tok->line++;

	tok->i++;
	tok->c = *tok->i;
	return to[tok->c]->Advance();
}

SimpleToken* Tokenizer::MultiLineComment::End::Advance()
{
	tok->i++;
	tok->c = *tok->i;
	return Start::Instance->Advance();
}

SimpleToken* Tokenizer::Operator::End::Advance()
{
	const char* value = tok->Names[tok->str];
	if (!value)
	{
		NamesIterator i = tok->Names.find(tok->str);
		value = i->second = i->first.c_str();
	}
	return new SimpleToken(SimpleToken::Operator,value, 0,0,tok->line);
}


void Tokenizer::Start::Init(Tokenizer* t)
{
	State::Init(t);

	for (char c = 0; c <= 32; c++)
		to[c] = WhiteSpace::Instance;

	for (char c = 'A'; c <= 'Z'; c++)
		to[c] = Name::Instance;

	for (char c = 'a'; c <= 'z'; c++)
		to[c] = Name::Instance;

	for (char c = '0'; c <= '9'; c++)
		to[c] = Integer::Instance;

	to['"'] = String::Instance;
	to['\''] = Char::Instance;
	to['_'] = Name::Instance;

	to['-'] = Operator_Minus::Instance;
	to['<'] = Operator_Less::Instance;
	to['>'] = Operator_More::Instance;
	to['+'] = Operator_Plus::Instance;
	to['&'] = Operator_And::Instance;
	to['!'] = Operator_PrefixDoubleEqual::Instance;
	to['%'] = Operator_PrefixEqual::Instance;
	to['*'] = Operator_Multiply::Instance;
	to['/'] = Operator_Slesh::Instance;
	to['='] = Operator_PrefixDoubleEqual::Instance;
	to['|'] = Operator_Or::Instance;
	to['^'] = Operator_PrefixEqual::Instance;
	to['~'] = Operator_PrefixEqual::Instance;

	to['.'] = Operator::Instance;
	to['#'] = Operator::Instance;
	to['$'] = Operator::Instance;
	to['('] = Operator::Instance;
	to[')'] = Operator::Instance;
	to[','] = Operator::Instance;
	to[':'] = Operator::Instance;
	to[';'] = Operator::Instance;
	to['?'] = Operator::Instance;
	to['@'] = Name::Instance;
	to['['] = Operator::Instance;
	to[']'] = Operator::Instance;
	to['{'] = Operator::Instance;
	to['}'] = Operator::Instance;

	to['\\'] = Error::Instance;
}

void Tokenizer::Name::Init(Tokenizer* t) 
{
	State::Init(t);

	char c;
	for (c = 0; c < 127; c++)
		to[c] = Name::End::Instance;

	for (c = 'A'; c <= 'Z'; c++)
		to[c] = Name::Instance;

	for (c = 'a'; c <= 'z'; c++)
		to[c] = Name::Instance;

	for (c = '0'; c <= '9'; c++)
		to[c] = Name::Instance;

	to['_'] = Name::Instance;
}


void Tokenizer::Integer::Init(Tokenizer* t)
{
	State::Init(t);

	char c;
	for (c = 0; c < 127; c++)
		to[c] = Integer::End::Instance;

	for (c = '0'; c <= '9'; c++)
		to[c] = Integer::Instance;

	for (c = 'A'; c <= 'Z'; c++)
		to[c] = Integer::Error::Instance;

	for (c = 'a'; c <= 'z'; c++)
		to[c] = Integer::Error::Instance;

	to['.'] = Fraction::Instance;
	to['e'] = Fraction_Exponent::Instance;
	to['E'] = Fraction_Exponent::Instance;
}

void Tokenizer::Fraction::Init(Tokenizer* t)
{
	State::Init(t);

	char c;
	for (c = 0; c < 127; c++)
		to[c] = Fraction::End::Instance;

	for (c = '0'; c <= '9'; c++)
		to[c] = Fraction::Instance;

	for (c = 'A'; c <= 'Z'; c++)
		to[c] = Fraction::Error::Instance;

	for (c = 'a'; c <= 'z'; c++)
		to[c] = Fraction::Error::Instance;

	to['e'] = Fraction_Exponent::Instance;
	to['E'] = Fraction_Exponent::Instance;
	to['.'] = Fraction::Error::Instance;
}

void Tokenizer::Fraction_Exponent::Init(Tokenizer* t)
{
	Fraction::Init(t);
	
	for (char c = '0'; c <= '9'; c++)
		to[c] = Fraction_Exponent::Instance;

	to['+'] = Fraction_Exponent::Sign::Instance;
	to['-'] = Fraction_Exponent::Sign::Instance;
	to['e'] = Fraction::End::Instance;
	to['E'] = Fraction::End::Instance;
}

void Tokenizer::Fraction_Exponent::Sign::Init(Tokenizer* t)
{
	Fraction::Init(t);

	for (char c = '0'; c <= '9'; c++)
		to[c] = Fraction_Exponent::Sign::Instance;

	to['e'] = Fraction::End::Instance;
	to['E'] = Fraction::End::Instance;
}

void Tokenizer::String::Init(Tokenizer* t)
{
	State::Init(t);
	for (char c = 0; c < 32; c++)
		to[c] = String::Error::Instance;

	for (char c = 32; c < 127; c++)
		to[c] = String::Instance;

	to['"'] = String::End::Instance;
	to['\\'] = String::Escapement::Instance;
}

void Tokenizer::String::Escapement::Init(Tokenizer* t)
{
	State::Init(t);
	for (char c = 0; c < 32; c++)
		to[c] = String::Error::Instance;

	for (char c = 32; c < 127; c++)
		to[c] = String::Instance;
}

void Tokenizer::Char::Init(Tokenizer* t)
{
	State::Init(t);
	for (char c = 0; c < 32; c++)
		to[c] = Char::Error::Instance;

	for (char c = 32; c < 127; c++)
		to[c] = String::Instance;

	to['\''] = Char::End::Instance;
	to['\\'] = Char::Escapement::Instance;
}

void Tokenizer::Char::Escapement::Init(Tokenizer* t)
{
	State::Init(t);
	for (char c = 0; c < 32; c++)
		to[c] = Char::Error::Instance;

	for (char c = 32; c < 127; c++)
		to[c] = Char::Instance;
}

void Tokenizer::Comment::Init(Tokenizer* t)
{
	State::Init(t);
	for (char c = 0; c < 127; c++)
		to[c] = Comment::Instance;

	to['\n'] = Comment::End::Instance;
	to['\r'] = Comment::End::Instance;
}

void Tokenizer::Operator::Init(Tokenizer* t)
{
	State::Init(t);
	for (char c = 0; c < 127; c++)
		to[c] = Operator::End::Instance;
}

void Tokenizer::Operator_Slesh::Init(Tokenizer* t)
{
	Operator::Init(t);
	
	to['/'] = Comment::Instance;
	to['*'] = MultiLineComment::Instance;
	to['='] = Operator::Instance;
	to['.'] = Operator::Instance;
}

void Tokenizer::Operator_Plus::Init(Tokenizer* t)
{
	Operator::Init(t);
	
	to['+'] = Operator::Instance;
	to['='] = Operator::Instance;
	to['.'] = Operator::Instance;
}

void Tokenizer::Operator_Minus::Init(Tokenizer* t)
{
	Operator::Init(t);
	
	to['-'] = Operator::Instance;
	to['='] = Operator::Instance;
	to['.'] = Operator::Instance;
	to['>'] = Operator::Instance;
}

void Tokenizer::Operator_Less::Init(Tokenizer* t)
{
	Operator::Init(t);
	
	to['<'] = Operator_PrefixEqual::Instance;
	to['='] = Operator::Instance;
}

void Tokenizer::Operator_More::Init(Tokenizer* t)
{
	Operator::Init(t);
	
	to['>'] = Operator_PrefixEqual::Instance;
	to['='] = Operator::Instance;
}

void Tokenizer::Operator_PrefixEqual::Init(Tokenizer* t)
{
	Operator::Init(t);
		
	to['='] = Operator::Instance;
}

void Tokenizer::Operator_PrefixDoubleEqual::Init(Tokenizer* t)
{
	Operator::Init(t);
		
	to['='] = Operator_PrefixEqual::Instance;
}

void Tokenizer::Operator_Multiply::Init(Tokenizer* t)
{
	Operator::Init(t);
		
	to['='] = Operator::Instance;
	to['.'] = Operator::Instance;
}
void Tokenizer::MultiLineComment::Init(Tokenizer* t)
{
	State::Init(t);
	for (char c = 0; c < 127; c++)
		to[c] = MultiLineComment::Instance;

	to['*'] = MultiLineComment_PreEnd::Instance;
}

void Tokenizer::MultiLineComment_PreEnd::Init(Tokenizer* t)
{
	MultiLineComment::Init(t);
	to['/'] = MultiLineComment::End::Instance;
}

void Tokenizer::Operator_And::Init(Tokenizer* t)
{
	Operator::Init(t);
	
	to['&'] = Operator_PrefixEqual::Instance;
	to['='] = Operator::Instance;
}

void Tokenizer::Operator_Or::Init(Tokenizer* t)
{
	Operator::Init(t);
	
	to['|'] = Operator_PrefixEqual::Instance;
	to['='] = Operator::Instance;
}



//old code
/*void tokenize(string code, string prefix, string suffix) 
{
	char c;                      // The current character.
	int from;                   // The index of the start of the token.
	int i = 0;                  // The index of the current character.
	int length = code.length();
//	int n;                      // The number value.
	char q;                      // The quote character.
	string str;                    // The string value.
	int line = 1;

// If prefix and suffix strings are not provided, supply defaults.

	if (prefix.empty()) {
		prefix = "<>+-&";
	}
	if (suffix.empty()) {
		suffix = "=>&:";
	}


// Loop through this text, one character at a time.

	c = code[i];
	while (c) {
		from = i;
		if (c == '\n')
			line++;

// Ignore whitespace.

		if (c <= ' ') {
			i += 1;
			c = code[i];

// name.

		} else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
			str = c;
			i += 1;
			while(true) {
				c = code[i];
				if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
						(c >= '0' && c <= '9') || c == '_') {
					str += c;
					i += 1;
				} else {
					break;
				}
			}
			tokens.push_back(SimpleToken("name", str, from, i, line));

// number.

// A number cannot start with a decimal point. It must start with a digit,
// possibly '0'.

		} else if (c >= '0' && c <= '9') {
			str = c;
			i += 1;

// Look for more digits.

			while(true) {
				c = code[i];
				if (c < '0' || c > '9') {
					break;
				}
				i += 1;
				str += c;
			}

// Look for a decimal fraction part.

			if (c == '.') {
				i += 1;
				str += c;
				while(true) {
					c = code[i];
					if (c < '0' || c > '9') {
						break;
					}
					i += 1;
					str += c;
				}
			}

// Look for an exponent part.

			if (c == 'e' || c == 'E') {
				i += 1;
				str += c;
				c = code[i];
				if (c == '-' || c == '+') {
					i += 1;
					str += c;
					c = code[i];
				}
				if (c < '0' || c > '9') {
					SimpleToken("number", str, from, i, line).error("Bad exponent");
				}
                
				i += 1;
				str += c;
				c = code[i];
				while (c >= '0' && c <= '9')
				{
					i += 1;
					str += c;
					c = code[i];
				}
			}

// Make sure the next character is not a letter.

			if (c >= 'a' && c <= 'z') {
				str += c;
				i += 1;
				SimpleToken("number", str, from, i, line).error("Bad number");
			}

// Convert the string value to a number. If it is finite, then it is a good
// token.

			//n = 0 + str;
			//if (isFinite(n)) {
				tokens.push_back(SimpleToken("number", str, from, i, line));
			//} else {
			//    SimpleToken('number', str).error("Bad number");
			//}

// string

		} else if (c == '\'' || c == '"') {
			str = "";
			q = c;
			i += 1;
			while(true) {
				c = code[i];
				if (c < ' ') {
					SimpleToken("string", str, from, i, line).error(c == '\n' || c == '\r' ?
						"Unterminated string." :
						"Control character in string.");
				}

// Look for the closing quote.

				if (c == q) {
					break;
				}

// Look for escapement.

				if (c == '\\') {
					i += 1;
					if (i >= length) {
						SimpleToken("string", str, from, i, line).error("Unterminated string");
					}
					c = code[i];
					switch (c) {
					case 'b':
						c = '\b';
						break;
					case 'f':
						c = '\f';
						break;
					case 'n':
						c = '\n';
						break;
					case 'r':
						c = '\r';
						break;
					case 't':
						c = '\t';
						break;
					case 'u':
						if (i >= length) {
							SimpleToken("string", str, from, i, line).error("Unterminated string");
						}
						sscanf(code.substr(i+1,4).c_str(),"%d",&c); 
						//if (!isFinite(c) || c < 0) {
						//	make('string', str).error("Unterminated string");
						//}
						//c = String.fromCharCode(c);
						i += 4;
						break;
					}
				}
				str += c;
				i += 1;
			}
			i += 1;
			tokens.push_back(SimpleToken("string", str, from, i, line));
			c = code[i];

// comment.

		} else if (c == '/' && code[i + 1] == '/') {
			i += 1;
			while(true) {
				c = code[i];
				if (c == '\n' || c == '\r') {
					break;
				}
				i += 1;
			}

// combining

		} else if (prefix.find_first_of(c) != string::npos ) {
			str = c;
			i += 1;
			while (i < length) {
				c = code[i];
				if (suffix.find_first_of(c) == string::npos) {
					break;
				}
				str += c;
				i += 1;
			}
			tokens.push_back(SimpleToken("operator", str, from, i, line));

// single-character operator

		} else {
			i += 1;
			str = c;
			tokens.push_back(SimpleToken("operator", str, from, i, line));
			c = code[i];
		}
	}
}*/
